// Global constants definitions
use std::collections::HashMap;
use std::fs::File;

use log::{error, info};
use serde::Deserialize;
use serde_yaml;

use super::datasource::DataSource;
use super::endpoint::EndPoint;
use super::task::Task;
use crate::endpoint::IndexeaIndex;

#[derive(Deserialize, PartialEq, Debug)]
pub struct IndexerConfig {
    datasource: HashMap<String, DataSource>,
    endpoint: EndPoint,
    tasks: HashMap<String, Task>,
}

impl IndexerConfig {
    pub fn parse(file: &str) -> Result<IndexerConfig, String> {
        match File::open(file) {
            Ok(f) => match serde_yaml::from_reader::<File, IndexerConfig>(f) {
                Ok(config) => Ok(config),
                Err(e) => {
                    println!("{:?}", e);
                    Err(e.to_string())
                }
            },
            Err(e) => Err(e.to_string()),
        }
    }

    pub fn test(&self) -> bool {
        let mut error = false;
        //test datasource
        if self.test_datasource() {
            info!("datasource {:?} test ok.", self.datasource.keys());
        } else {
            error = true;
        }
        //test endpoint
        if self.test_endpoint() {
            info!("endpoint [\"{}\"] test ok.", self.endpoint.url);
        } else {
            error = true;
        }
        //test tasks
        if self.test_tasks() {
            info!("tasks {:?} test ok.", self.tasks.keys());
        } else {
            error = true;
        }
        !error
    }

    fn test_datasource(&self) -> bool {
        let mut error = false;
        for ds in self.datasource.iter() {
            match ds.1.inst() {
                Ok(ids) => match ids.ping() {
                    Ok(_res) => {}
                    Err(err) => {
                        error = true;
                        error!("datasource [\"{}\"] test failed, reason: {}", ds.0, err);
                    }
                },
                Err(e) => {
                    error!("datasource [\"{}\"] test failed, reason: {}", ds.0, e);
                }
            }
        }
        !error
    }

    fn test_endpoint(&self) -> bool {
        if let Err(err) = self.endpoint.test() {
            error!("endpoint [\"{}\"] test failed, reason: {}", self.endpoint.url, err);
            return false;
        };
        return true;
    }

    fn test_tasks(&self) -> bool {
        let mut error = false;
        for task in self.tasks.iter() {
            if !self.datasource.contains_key(&task.1.datasource) {
                error = true;
                error!(
                    "task [\"{}\"] has undefined datasource [\"{}\"]",
                    task.0, &task.1.datasource
                );
                continue;
            }
            if !self.endpoint.indices.contains_key(&task.1.index) {
                error = true;
                error!("task [\"{}\"] has undefined index [\"{}\"]", task.0, &task.1.index);
                continue;
            }
            match task.1.test() {
                Ok(_res) => {}
                Err(err) => {
                    error = true;
                    error!("tasks {} test failed, reason: {}", task.0, err);
                }
            }
        }
        !error
    }

    pub fn datasource(&self, name: &String) -> Option<&DataSource> {
        self.datasource.get(name)
    }

    pub fn endpoint(&self) -> &EndPoint {
        &self.endpoint
    }

    pub fn index(&self, name: &String) -> Option<&IndexeaIndex> {
        self.endpoint.indices.get(name)
    }

    pub fn tasks(&self) -> &HashMap<String, Task> {
        &self.tasks
    }
}

impl Clone for IndexerConfig {
    fn clone(&self) -> IndexerConfig {
        let data = IndexerConfig {
            datasource: self.datasource.clone(),
            endpoint: self.endpoint.clone(),
            tasks: self.tasks.clone(),
        };
        data
    }
}
